-module(container_manager).

-behaviour(gen_server).

%% API
-export([start_link/0]).
-export([add_container/1 ]).
-export([stop_container/1]).
-export([query_container/0,query_container/1]).
%%-export([lookup/2]).
%%-export([setup/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {}).

-include("xengine.hrl").


-define(SERVER, ?MODULE).
-include_lib("stdlib/include/qlc.hrl").


%%lookup(container_runtime,Name)->
%%	configuration_manager:do(qlc:q([X || X <- mnesia:table(container_runtime), X#container_runtime.name=:=Name])).




query_container()->
        gen_server:call(?MODULE, { query_container }).
query_container(ContainerName)->
        gen_server:call(?MODULE, { query_container , ContainerName }).

%%新增container并启动
add_container(FileName) when is_list(FileName) ->
%%        error_logger:info_msg("add_container from file~n"),
        case file:consult(FileName) of
                { ok , Containers } when is_list(Containers)  ->
                        error_logger:info_msg("Containers=~p~n",[Containers]),
                        F = fun(Container) ->
                                add_container(Container)
                	end,
                        lists:foreach(F , Containers),
                        ok;
                { error , Reason } ->
                        { error ,Reason }
        end;
add_container(Container) when is_record(Container,container) ->
%%        error_logger:info_msg("container_manager:Container=~p~n",[Container]),
	gen_server:call(?MODULE, {add , Container} ).



stop_container(ContainerName) ->
       gen_server:call(?MODULE, {stop_container , ContainerName}  , 5000 ).


%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
    error_logger:info_msg("starting container_manager .....~n"),
    case   lookup_all_containers() of
        [] ->
                    error_logger:info_msg("start container_manager success~n"),
                    {ok, #state{}};
        Containers ->
                    case   do_start_container(Containers)  of
                        ok ->
                               error_logger:info_msg("start container_manager success~n"),
                               { ok , #state{}};
                        { error ,Reason } ->
                                error_logger:info_msg("start container_manager fail in ~p~n",[Reason]),
                                { stop , Reason }
                    end
    end.


handle_call({query_container }, _From ,State ) ->
        CR =  configuration_manager:do(qlc:q([{X,Y#container_runtime.runtime} ||
                                                     X <- mnesia:table(container),
        	                                     Y <- mnesia:table(container_runtime),
        	                                     X#container.name=:=Y#container_runtime.name])) ,
        
        {reply ,  lists:map( fun ({Container,Runtime}) ->
                                        {Container,Runtime,
                                         length(instance_manager:lookup_apps_by_container(Container#container.name))
                                         }
                                end ,   CR)               , State};
 
handle_call({query_container, ContainerName } , _From ,State ) ->
        case  configuration_manager:do(qlc:q([{X,Y#container_runtime.runtime} ||
                                                     X <- mnesia:table(container),
        	                                     Y <- mnesia:table(container_runtime),
        	                                     X#container.name=:=Y#container_runtime.name,
        	                                     X#container.name=:=ContainerName])) of
                [] ->
                        {reply ,  {error, "no such a container named:"++ContainerName } , State };
                [{Container,Runtime}] ->
        %%                error_logger:info_msg("~p,~p ~n",[Container , is_list(Container#container.port)]),
                        
                        { reply ,  
                          { Container,
                            Runtime, 
                            length( instance_manager:lookup_apps_by_container(ContainerName) )
                          },
                          State}
                        
        end;

handle_call({add ,Container},_From,State) ->
	case configuration_manager:insert(Container)  of
	    ok ->
		{reply,do_start_container(Container),State } ;
	    Any ->
	        
		{ reply , {error , Any} , State }
	end;
handle_call({stop_container,ContainerName}=Msg , _From , State ) ->	
        error_logger:info_msg("container_manager:recving a msg=~p~n",[Msg]),
        case configuration_manager:lookup(container, ContainerName) of
                [] ->
                   {reply ,  {error, "no such a container named:"++ContainerName } , State };
                _ ->
                   instance_manager:undeploy(container,ContainerName),
                   { reply, container:stop(ContainerName),State}
        
        end;                
        
        
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
%%%%某个容器上报负荷很小
%%handle_cast( {alarm_cpu_minimum ,  
%%                                                                { InstanceRuntimes ,
%%                                                                  ContainerRuntime , 
%%                                                                  Container    }   
%%                                                                   } ,State ) ->
%%    error_logger:info_msg("container_manager:recving a alarm_cpu_minium message , InstanceRuntimes=~p , ContainerRuntime=~p,Container=~p~n",[InstanceRuntimes,ContainerRuntime,Container]),
        
%%        Fun = fun(InstanceRuntime , {C,CR} ) ->
%%               #instance_runtime{instance=Instance, runtime=Runtime }=InstanceRuntime,
                
                
%%                LastScableTime =  get_last_app_scable_time(Instance#instance.app_name),
%%                CurrentTime =  calendar:datetime_to_gregorian_seconds(calendar:local_time()),
                
              
                
%%                case   CurrentTime - LastScableTime  > 10 of
%%                        false ->    
%%                           error_logger:info_msg("LastScableTime=~p,CurrentTime=~p~n" ,
%%                                                        [LastScableTime,CurrentTime]),
%%                                 {C,CR}  ;
%%                        true ->
%%                           AppInstanceNum =  length(instance_manager:lookup_containers_by_app(Instance#instance.app_name) ),
                                                

%%                                    error_logger:info_msg("the app ~p has ~p instances , neede scable~n ",
%%                                                [Instance#instance.app_name, AppInstanceNum]),
                               
%%                                     configuration_manager:insert(#app_scable_log{app=Instance#instance.app_name,         type="s",time=CurrentTime}),
                                
%%                                     DeltaCpu = (CR#container_runtime.runtime)#runtime.cpu *    Runtime#runtime.cpu/(AppInstanceNum-1) ,                                 
%%                                      case  configuration_manager:do( 
%%                                                qlc:q([X  || 
%%                                                      X <- mnesia:table(instance),
%%                                                      Y <- mnesia:table(container_runtime),
%%                                                      X#instance.app_name=:=Instance#instance.app_name,
%%                                                      X#instance.container_name=:=Y#container.name,
%%                                                     (Y#container_runtime.runtime)#runtime.cpu

%%                                                          > ?CPU_MID_THRESHOLD - DeltaCpu 
%%                                                        ]))   of
%%                                          [] ->  
%%                                             error_logger:info_msg("app instance too many , undeploy instance =~p~n",[Instance] ),
                                             %%可以撤销
%%                                             gen_server:cast(instance_manager, {undeploy , 
%%                                                                                {Instance#instance.app_name,
%%                                                                                 Instance#instance.container_name} }),
%%                                             {C,CR};                                        
%%                                          Any  ->
%%                                             error_logger:info_msg("can't  undeploy instance =~p , Any=~p~n",[Instance,Any] ),
%%                                             error_logger:info_msg("all container_runtimes are ~p~n",[ configuration_manager:do( 
%%                                                qlc:q([X  || 
%%                                                      X <- mnesia:table(instance),
%%                                                      Y <- mnesia:table(container_runtime),
%%                                                      X#instance.app_name=:=Instance#instance.app_name,
%%                                                      X#instance.container_name=:=Y#container.name
%%                                                        ]))]),


 %%                                 false ->
                                     %%     error_logger:info_msg("the app ~p has ~p instances , needn't scable~n ",
                                     %%                   [Instance#instance.app_name, AppInstanceNum]),
                                   %%将应用迁移到                     
 %%                                         {C,CR}   
  %%                         end
    %%                 end
     %%           end,
     %%     lists:foldl(Fun, {Container , ContainerRuntime}  , InstanceRuntimes ),
%%
handle_cast( {alarm_cpu_ceiling ,{ InstanceRuntimes ,
                              ContainerRuntime , 
                              _Container    }  } , State ) ->
 %%     error_logger:info_msg("container_manager:recving a alarm_cpu_ceiling message , InstanceRuntimes=~p , ContainerRuntime=~p,Container=~p~n",[InstanceRuntimes,ContainerRuntime,Container]),
                              
        Fun=  fun(InstanceRuntime1,InstanceRuntime2)->
                (InstanceRuntime1#instance_runtime.runtime)#runtime.cpu
                         >                
                          (InstanceRuntime2#instance_runtime.runtime)#runtime.cpu
        end,
        
     OrderedInstanceRuntimes = lists:sort(Fun,InstanceRuntimes),
%%     error_logger:info_msg("container_manager:OrderedInstanceRuntimes=~p~n",[OrderedInstanceRuntimes]),
     
     scable_container(OrderedInstanceRuntimes,ContainerRuntime,0),
     
     {noreply , State}  ;


handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_start_container([]) ->
        ok;
do_start_container( [H|L] =Containers) when is_list(Containers) ->
        
        case  do_start_container(H) of
                {error, Reason } ->
                                error_logger:info_msg("starting container:~p error: Reason~n",[H,Reason]),
                                do_start_container(L) ;
                ok ->
                                error_logger:info_msg("starting container:~p success~n",[H]),
                                do_start_container(L) 
        end;
do_start_container(Container)->
	case supervisor:start_child(container_sup, [Container]) of
                {ok , _ } ->
			 configuration_manager:insert(#container_runtime{
								name=Container#container.name,
								runtime=#runtime{}}),
			 ok;
       		{ok,_,_ } ->
			 configuration_manager:insert(#container_runtime{
								name=Container#container.name,
								runtime=#runtime{}}),
			 ok;
	       { error ,   {already_started,_} } ->
                         error_logger:info_msg("resuming the  container manager ~n"),
	                 ok;
    	       {error ,Reason } ->
      		         {error,Reason}   
	end.

%%do_select_container(App,Num) ->
%%        deploy_policy:select_container(App,Num).


get_last_app_scable_time(AppName)->
         case    configuration_manager:do
                                (qlc:q([X#app_scable_log.time ||
                                                 X <- mnesia:table(app_scable_log), 
                                                 X#app_scable_log.app=:=AppName
                                                ]))  of
                                     [] -> 0 ;
                                     [Any] -> Any 
          end.

	 
scable_container([],_ContainerRuntime,_Acc )   ->
        ok;
scable_container([H|L]=_InstanceRuntimes,ContainerRuntime,Acc) when  Acc < 30.0 ->
                        
                      Instance=H#instance_runtime.instance,
                       LastScableTime =  get_last_app_scable_time(Instance#instance.app_name),
                        CurrentTime =  calendar:datetime_to_gregorian_seconds(calendar:local_time()),
                        
                        case   CurrentTime - LastScableTime  > 10 of
                                false ->
                                        error_logger:info_msg("container_manager: current time is ~p , last deploy/undeploy time is ~p, needn't scable ~n",[CurrentTime,LastScableTime]),
                                        scable_container(L,ContainerRuntime,Acc );
                                true ->
                                 %%        error_logger:info_msg("container_manager: current time is ~p , last deploy/undeploy time is ~p, need scable ~n",[CurrentTime,LastScableTime]),
                                        case   app_manager:lookup(Instance#instance.app_name) of
                                           [] ->
                                               error_logger:info_msg("container_manager: no such a app named:~p~n",[Instance#instance.app_name]),
                                                scable_container(L,ContainerRuntime,Acc );
                                           [App] ->
                                                   configuration_manager:insert(#app_scable_log{app=Instance#instance.app_name,         type="s",time=CurrentTime}),
                                                case  deploy_policy:select_container(App, H , ContainerRuntime ) of
                                                        undefined ->
                                                             send_alarm(),
                                                             scable_container(L,ContainerRuntime,Acc );
                                                        ContainerName ->
                                                            gen_server:cast(instance_manager , {deploy,{App,ContainerName } } ),
                                                            scable_container(L,ContainerRuntime,
                                                                Acc +  (ContainerRuntime#container_runtime.runtime)#runtime.cpu*(H#instance_runtime.runtime)#runtime.cpu)
                                        
                                                end                
                                        end
                         end;

scable_container(_InstanceRuntimes,_ContainerRuntime,_Acc )   ->
        ok.         
              
lookup_all_containers() ->
        configuration_manager:do(qlc:q([X || X <- mnesia:table(container)])).


%%todo，以后统一增加报警功能
send_alarm()->
        error_logger:info_msg("container_manager:can't select any container!~n"),
        ok.
